package com.sheepone.util.beanconvert.doublearray;

import com.sheepone.util.ObjectUtil;
import com.sheepone.util.ReflectUtil;
import com.sheepone.util.ArrayUtil;
import com.sheepone.util.anno.Ignore;
import com.sheepone.util.anno.Index;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;


/**
 * @author Miss.杨
 * @description
 * @see Index ;
 * @see Ignore ;
 * @see ArrayUtil ;
 * @since 2024/2/22 - 16:44
 */
public class DoubleArrayFieldCovertMapping<T> { // todo 开发忽略静态字段的功能

    private final static Comparator<Field> comparator = (field1, field2) -> {
        int index = getIndexValue(field1);
        return index - getIndexValue(field2);
        // 具有较低索引值的Field对象将排在前面
    };

    private final String[] environments;
    private final Field[] fields;

    private final Class[] fieldTypes;

    private final int[] indexNums;

    private final int totalNum;

    private final Class<T> clazz;

    private static int getIndexValue(Field field) {
        Index declaredAnnotation = field.getDeclaredAnnotation(Index.class);
        return declaredAnnotation == null ? Integer.MAX_VALUE : declaredAnnotation.value();
    }

    private Field[] initFields(Field[] declaredFields) {
        ReflectUtil.accessible(declaredFields);
        Field[] fields = validFields(declaredFields);
        Arrays.sort(fields, comparator);
        return fields;
    }

    private int[] initNums(Field[] fields, Object object) {
        int[] ints = new int[fields.length];
        for (int i = 0; i < fields.length; i++) {
            try {
                Object obj = fields[i].get(object);
                ints[i] = fieldTypes[i].isArray() ? ((Object[]) obj).length : 1;
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
        return ints;
    }

    private Field[] validFields(Field[] fields) {
        ArrayList<Field> fieldList = new ArrayList<>();
        for (Field field : fields) {
            Class<?> type = field.getType();
            Ignore ignore = field.getDeclaredAnnotation(Ignore.class);
            if (ignore != null && ArrayUtil.isIntersecting(environments, ignore.environments())) {
                continue;
            }
            // ignore == null || !ArrayUtil.isIntersecting(environments, ignore.environments())
            if (!ObjectUtil.isNumericType(type)) {
                throw new ClassCastException("The field type of " + field.getName() + " must be a numerical type");
            }
            fieldList.add(field);
        }
        return fieldList.toArray(new Field[0]);
    }

    public DoubleArrayFieldCovertMapping(T[] objects) {
        this(objects[0]);
    }

    public DoubleArrayFieldCovertMapping(List<T> objects) {
        this(objects.get(0));
    }

    public DoubleArrayFieldCovertMapping(List<T> objects, String... environment) {
        this(objects.get(0), environment);
    }

    public DoubleArrayFieldCovertMapping(T[] objects, String... environments) {
        this(objects[0], environments);
    }

    public DoubleArrayFieldCovertMapping(T object, String... environments) {
        this.clazz = (Class<T>) object.getClass();
        this.environments = checkEnvironments(environments);
        Field[] declaredFields = this.clazz.getDeclaredFields();
        this.fields = initFields(declaredFields);
        this.fieldTypes = new Class[fields.length];
        for (int i = 0; i < fields.length; i++) {
            fieldTypes[i] = fields[i].getType();
        }
        this.indexNums = initNums(fields, object);
        this.totalNum = Arrays.stream(indexNums).sum();
    }

    private String[] checkEnvironments(String[] environments) {
        if (environments == null || environments.length == 0) {
            return new String[]{Ignore.ANY_ENVIRONMENT};
        } else {
            String[] values = Arrays.copyOf(environments, environments.length + 1);
            values[values.length - 1] = Ignore.ANY_ENVIRONMENT;
            return values;
        }
    }


    public Field[] getFields() {
        return fields;
    }

    public Class[] getFieldTypes() {
        return fieldTypes;
    }

    public int[] getIndexNums() {
        return indexNums;
    }

    public int getTotalNum() {
        return totalNum;
    }

    public Class<T> getClazz() {
        return clazz;
    }

    public int getFieldStartIndex(String fieldName) {
        int index = 0;
        for (int i = 0; i < fields.length; i++) {
            if (!fields[i].getName().equals(fieldName)) {
                index += indexNums[i];
            } else {
                break;
            }
        }
        return index;
    }
}